﻿using Colorful.Caching;
using Colorful.Furniture.UserManagement;
using FreeSql;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using Newtonsoft.Json;
using SKIT.FlurlHttpClient.Wechat.Api;
using SKIT.FlurlHttpClient.Wechat.Api.Models;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;
using Colorful.Furniture.PermissionManagement;
using Volo.Abp;
using Volo.Abp.Application.Services;
using Volo.Abp.Security.Claims;

namespace Colorful.Furniture.MpManagement;

/// <summary>
///  微信公众号用户
/// </summary>
public class WechatMpUserAppService : ApplicationService
{
    private readonly IWechatMpExtensions _wechatMpExtensions;
    private readonly WechatApiClient _wechatMpClient;
    private readonly WechatMpOptions _wechatMpOptions;
    private readonly ColorfulJwtOptions _colorfulJwtOptions;
    private readonly IBaseRepository<User> _userRepository;
    private readonly IBaseRepository<UserIdentity> _userIdentityRepository;
    private readonly IFreeSql _freeSql;
    private readonly IRedisCache _redisCache;
    private readonly ILogger<WechatMpUserAppService> _logger;

    public WechatMpUserAppService(
        IWechatMpExtensions wechatMpExtensions,
        WechatApiClient wechatMpClient,
        IOptions<WechatMpOptions> wechatMpOptions,
        ILogger<WechatMpUserAppService> logger,
        IRedisCache redisCache,
        IOptions<ColorfulJwtOptions> colorfulJwtOptions,
        IBaseRepository<User> userRepository,
        IBaseRepository<UserIdentity> userIdentityRepository,
        IFreeSql freeSql)
    {
        _wechatMpExtensions = wechatMpExtensions;
        _wechatMpClient = wechatMpClient;
        _logger = logger;
        _redisCache = redisCache;
        _userRepository = userRepository;
        _userIdentityRepository = userIdentityRepository;
        _freeSql = freeSql;
        _colorfulJwtOptions = colorfulJwtOptions.Value;
        _wechatMpOptions = wechatMpOptions.Value;
    }

    /// <summary>
    ///  获取所有的用户的信息
    /// </summary>
    /// <returns></returns>
    [RemoteService(IsEnabled = false)]
    public async Task<List<CgibinUserInfoBatchGetResponse.Types.User>> GetAllListAsync()
    {
        var batchGetUserInfoDataList = await GetUserOpenIdListAsync();

        var userInfoList = new List<CgibinUserInfoBatchGetResponse.Types.User>();

        const int limitCount = 100;

        var count1 = batchGetUserInfoDataList.Count / limitCount;

        var count2 = batchGetUserInfoDataList.Count % limitCount;

        var pageRecords = count1 + (count2 > 0 ? 1 : 0);

        for (var i = 0; i < pageRecords; i++)
        {
            var result = await _wechatMpClient.ExecuteCgibinUserInfoBatchGetAsync(new CgibinUserInfoBatchGetRequest
            {
                AccessToken = await _wechatMpExtensions.GetAccessTokenAsync(),
                UserList = batchGetUserInfoDataList.Skip(i * limitCount).Take(100).Select(x =>
                    new CgibinUserInfoBatchGetRequest.Types.User
                    {
                        OpenId = x
                    }).ToList()
            });

            _wechatMpExtensions.HandleWxJsonResult(result);

            userInfoList.AddRange(result.UserList);
        }

        return userInfoList;
    }

    /// <summary>
    ///  获取微信授权地址，获取用户详情地址
    /// </summary>
    /// <returns></returns>
    public string GetAuthorizeUrl(string redirectUrl)
    {
        if (string.IsNullOrWhiteSpace(redirectUrl))
            throw new UserFriendlyException(@"请传入回调微信授权完成回调地址");

        var auth2AuthorizeUrl =
            _wechatMpClient.GenerateParameterizedUrlForConnectOAuth2Authorize(redirectUrl, "snsapi_userinfo");

        return auth2AuthorizeUrl;
    }

    /// <summary>
    ///  获取所有关注用户的编码
    /// </summary>
    /// <returns></returns>
    public async Task<List<string>> GetUserOpenIdListAsync()
    {
        var openIdResultJson = await _wechatMpClient.ExecuteCgibinUserGetAsync(new CgibinUserGetRequest
        {
            AccessToken = await _wechatMpExtensions.GetAccessTokenAsync()
        });

        _wechatMpExtensions.HandleWxJsonResult(openIdResultJson);

        return openIdResultJson.Data.OpenIdList.ToList();
    }

    /// <summary>
    ///  微信公众号用户登录
    /// </summary>
    /// <returns></returns>
    [RemoteService(IsEnabled = false)]
    public async Task<string> LoginAsync(string code)
    {
        //通过微信公众号授权code获取用户编码
        var (userId, snsUserInfo) = await GetUserIdAsync(code);

        _logger.LogInformation($"写入到用户申明的中的用户编码为:{userId}");

        //写入相应的claim信息到请求安全体中
        var userClaims = new List<Claim>
        {
            //将name值修改赋值成昵称
            new(AbpClaimTypes.SurName, snsUserInfo.Nickname),
            new(AbpClaimTypes.UserName, snsUserInfo.OpenId),
            new(AbpClaimTypes.UserId, userId.ToString()),
            new(AbpClaimTypes.Role, "User")
        };

        var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_colorfulJwtOptions.SecurityKey));
        //jwt证书
        var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
        var securityToken = new JwtSecurityToken(
            _colorfulJwtOptions.Issuer,
            _colorfulJwtOptions.Audience,
            userClaims,
            DateTime.Now,
            DateTime.Now.AddYears(2),
            credentials);

        var token = new JwtSecurityTokenHandler().WriteToken(securityToken);

        return token;
    }

    /// <summary>
    ///  通过code获取用户id
    /// </summary>
    /// <param name="code"></param>
    /// <returns>item1 用户编码；item2:微信公众号授权回调返回的用户信息</returns>
    [RemoteService(IsEnabled = false)]
    public async Task<Tuple<Guid, SnsUserInfoResponse>> GetUserIdAsync(string code)
    {
        if (string.IsNullOrWhiteSpace(code))
            throw new UserFriendlyException("请传入微信公众号登录凭证");

        //获取用户接口访问凭证信息
        var (accessToken, openId) = await GetUserAccessTokenAsync(code);

        //获取用户信息
        var snsUserInfo = await _wechatMpClient.ExecuteSnsUserInfoAsync(new SnsUserInfoRequest
        {
            AccessToken = accessToken,
            OpenId = openId
        });

        _wechatMpExtensions.HandleWxJsonResult(snsUserInfo);

        _logger.LogInformation($"获取到微信授权用户信息为:{JsonConvert.SerializeObject(snsUserInfo)}");

        //判断当前数据库中是否已存在当前用户如果存在则更新用户的头像信息即可，不存在就创建用户信息并将用户信息（user表的信息）写入到JwtToken中
        var userOpenIdIdentity = await _userIdentityRepository
            .Where(x => x.Credential == snsUserInfo.OpenId && x.IdentityType == IdentityType.微信公众号)
            .ToOneAsync();

        //用户编码
        Guid userId;

        //用户未被删除
        if (userOpenIdIdentity != null && userOpenIdIdentity.UserId.HasValue)
        {
            var user = new User
            {
                Id = userOpenIdIdentity.UserId.Value
            };
            userId = user.Id;
            _userRepository.Attach(user);

            //更新用户的头像和昵称
            user.AvatarUrl = snsUserInfo.HeadImageUrl;
            user.NickName = snsUserInfo.Nickname;

            await _userRepository.UpdateAsync(user);

            //更新认证者信息
            if (userOpenIdIdentity.Identifier != snsUserInfo.Nickname)
            {
                userOpenIdIdentity.Identifier = snsUserInfo.Nickname;
                await _userIdentityRepository.UpdateAsync(userOpenIdIdentity);
            }
        }
        else
        {
            //创建事务
            using var unitOfWork = _freeSql.CreateUnitOfWork();

            _userRepository.UnitOfWork = unitOfWork;
            _userIdentityRepository.UnitOfWork = unitOfWork;

            var insertUser = new User
            {
                AddTime = DateTime.Now,
                Amount = 0,
                Point = 0,
                AvatarUrl = snsUserInfo.HeadImageUrl,
                //微信调整，字段废弃详见：https://developers.weixin.qq.com/community/develop/doc/00028edbe3c58081e7cc834705b801
                //Sex强制返回 0、City、Province、country 强制返回“”
                Sex = "未知",
                IsDeleted = false,
                Status = 1, //已审核
                NickName = snsUserInfo.Nickname
            };

            var insert = await _userRepository.InsertAsync(insertUser);

            var insertUserIdentity = new UserIdentity
            {
                UserId = insert.Id,
                Credential = snsUserInfo.OpenId,
                Identifier = snsUserInfo.Nickname,
                IdentityType = IdentityType.微信公众号
            };

            await _userIdentityRepository.InsertAsync(insertUserIdentity);

            unitOfWork.Commit();

            userId = insertUser.Id;
        }

        return Tuple.Create(userId, snsUserInfo);
    }

    /// <summary>
    ///  获取用户信息的accessToken
    /// </summary>
    /// <param name="code"></param>
    /// <returns></returns>
    [RemoteService(IsEnabled = false)]
    public async Task<Tuple<string, string>> GetUserAccessTokenAsync(string code)
    {
        var authAccessTokenCache = await _redisCache.GetAsync<WechatUserOauthOutputDto>($"{CachePrefixConst.WechatMpOAuthAccessToken}:{_wechatMpOptions.AppId}:{code}");

        if (authAccessTokenCache == null)
        {
            //通过 code 换取网页授权access_token
            /*
             首先请注意，这里通过 code 换取的是一个特殊的网页授权access_token,与基础支持中的access_token（该access_token用于调用其他接口）不同。公众号可通过下述接口来获取网页授权access_token。如果网页授权的作用域为snsapi_base，则本步骤中获取到网页授权access_token的同时，也获取到了openid，snsapi_base式的网页授权流程即到此为止。
             */
            var accessTokenByCodeResponse =
                await _wechatMpClient.ExecuteSnsOAuth2AccessTokenAsync(new SnsOAuth2AccessTokenRequest
                {
                    Code = code
                });

            _wechatMpExtensions.HandleWxJsonResult(accessTokenByCodeResponse);

            authAccessTokenCache = new WechatUserOauthOutputDto
            {
                AccessToken = accessTokenByCodeResponse.AccessToken,
                OpenId = accessTokenByCodeResponse.OpenId
            };

            try
            {
                await _redisCache.SetAsync($"{CachePrefixConst.WechatMpOAuthAccessToken}:{_wechatMpOptions.AppId}:{code}", authAccessTokenCache, 120);
            }
            catch (Exception e)
            {
                _logger.LogError($"微信公众号用户网页授权TOKEN缓存插入失败：{e}");
            }
        }

        return Tuple.Create(authAccessTokenCache.AccessToken, authAccessTokenCache.OpenId);
    }

}